﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace ESoft.Core.Util.Extensions
{
    public static class TypeExtensions
    {
        /// <summary>
        ///     判断当前类型的对象能分配于指定泛型类型
        /// </summary>
        /// <param name="givenType">给定类型</param>
        /// <param name="genericType">泛型类型</param>
        /// <returns><c>true</c> if [is assignable to generic type] [the specified generic type]; otherwise, <c>false</c>.</returns>
        public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
        {
            if (!genericType.IsGenericType) return false;
            var interfaceTypes = givenType.GetInterfaces();
            if (interfaceTypes.Any(m => m.IsGenericType && m.GetGenericTypeDefinition() == genericType)) return true;
            if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) return true;
            var baseType = genericType.BaseType;
            if (baseType == null) return false;
            return IsAssignableToGenericType(baseType, genericType);
        }

        #region Property

        /// <summary>
        ///     获取所有的属性
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>PropertyInfo[].</returns>
        public static PropertyInfo[] GetAllProperties(this Type type)
        {
            if (type.IsInterface)
            {
                var propertyInfos = new List<PropertyInfo>();
                var considered = new List<Type>();
                var queue = new Queue<Type>();
                considered.Add(type);
                queue.Enqueue(type);
                while (queue.Count > 0)
                {
                    var subType = queue.Dequeue();
                    foreach (var subInterface in subType.GetInterfaces())
                    {
                        if (considered.Contains(subInterface)) continue;
                        considered.Add(subInterface);
                        queue.Enqueue(subInterface);
                    }
                    var typeProperties = subType.GetProperties(BindingFlags.FlattenHierarchy
                                                               | BindingFlags.Public | BindingFlags.NonPublic |
                                                               BindingFlags.Instance);
                    var newPropertyInfos = typeProperties.Where(m => propertyInfos.Contains(m));
                    propertyInfos.InsertRange(0, newPropertyInfos);
                }
            }

            return type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic |
                                      BindingFlags.Instance);
        }

        /// <summary>
        ///     获取所有公开属性
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>PropertyInfo[].</returns>
        public static PropertyInfo[] GetPublicProperties(this Type type)
        {
            if (type.IsInterface)
            {
                var propertyInfos = new List<PropertyInfo>();

                var considered = new List<Type>();
                var queue = new Queue<Type>();
                considered.Add(type);
                queue.Enqueue(type);
                while (queue.Count > 0)
                {
                    var subType = queue.Dequeue();
                    foreach (var subInterface in subType.GetInterfaces())
                    {
                        if (considered.Contains(subInterface)) continue;

                        considered.Add(subInterface);
                        queue.Enqueue(subInterface);
                    }

                    var typeProperties = subType.GetProperties(
                        BindingFlags.FlattenHierarchy
                        | BindingFlags.Public
                        | BindingFlags.Instance);

                    var newPropertyInfos = typeProperties
                        .Where(x => !propertyInfos.Contains(x));

                    propertyInfos.InsertRange(0, newPropertyInfos);
                }

                return propertyInfos.ToArray();
            }

            return type.GetProperties(BindingFlags.FlattenHierarchy
                                      | BindingFlags.Public | BindingFlags.Instance);
        }
        /*
        public static T GetCustomAttribute<T>(this PropertyInfo propertyInfo, bool inherit) where T : Attribute
        {
            return GetCustomAttributes<T>(propertyInfo, inherit).FirstOrDefault();
        }
        public static IEnumerable<T> GetCustomAttributes<T>(this PropertyInfo propertyInfo, bool inherit)
            where T : Attribute
        {
             
            return propertyInfo.GetCustomAttributes(typeof(T), inherit).OfType<T>();
        }

        public static bool HasCustomAttribute<T>(this PropertyInfo property, bool inherit) where T : Attribute
        {
            return property.GetCustomAttributes<T>(inherit).Any();
        }
        */
        #endregion Property

        #region Assembly

        #endregion

        #region Attribute

        public static T GetCustomAttribute<T>(this Type type, bool inherit = true)
            where T : Attribute
        {
            return type.GetCustomAttributes<T>(inherit).FirstOrDefault();
        }

        public static IEnumerable<T> GetCustomAttributes<T>(this Type type, bool inherited = true)
            where T : Attribute
        {
            if (type == null) return Enumerable.Empty<T>();
            return type.GetCustomAttributes(typeof(T), inherited).OfType<T>();
        }

        #endregion Attribute

        #region Type

        public static object GetDefaultValue(this Type type)
        {
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }

        /// <summary>
        ///     判断是否为数值类型
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if the specified type is numeric; otherwise, <c>false</c>.</returns>
        public static bool IsNumeric(this Type type)
        {
            return type == typeof(byte)
                   || type == typeof(short)
                   || type == typeof(int)
                   || type == typeof(long)
                   || type == typeof(sbyte)
                   || type == typeof(ushort)
                   || type == typeof(uint)
                   || type == typeof(ulong)
                   || type == typeof(decimal)
                   || type == typeof(double)
                   || type == typeof(float);
        }

        /// <summary>
        /// 判断是否为Boolean类型
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if the specified type is bool; otherwise, <c>false</c>.</returns>
        public static bool IsBool(this Type type)
        {
            return type.GetNonNullableType() == typeof(bool);
        }

        /// <summary>
        ///     是否基本数据类型
        /// </summary>
        public static bool IsBasic(this Type type)
        {
            return type.IsValueType || type == typeof(string);
        }

        /// <summary>
        ///     判断是否为值类型
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if [is value type] [the specified type]; otherwise, <c>false</c>.</returns>
        public static bool IsValueType(this Type type)
        {
            return type.IsValueType || type.IsPrimitive;
        }

        public static bool IsNullOrDefault<T>(this T? value) where T : struct
        {
            return default(T).Equals(value.GetValueOrDefault());
        }

        /// <summary>
        ///     判断是否为匿名类型
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if [is anonymous type] [the specified type]; otherwise, <c>false</c>.</returns>
        /// <exception cref="System.ArgumentNullException">type</exception>
        public static bool IsAnonymousType(this Type type)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));
            return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
                   && type.IsGenericType && type.Name.Contains("AnonymousType")
                   && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
                   && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
        }
        /// <summary>
        ///  判断是否为委托类型
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if the specified type is delegate; otherwise, <c>false</c>.</returns>
        public static bool IsDelegate(this Type type)
        {
            return type.IsSubclassOf(typeof(MulticastDelegate));
        }

        /// <summary>
        ///     是否Nullable&lt;T&gt;类型
        /// </summary>
        public static bool IsNullableType(this Type type)
        {
            return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        /// <summary>
        ///     如果类型是Nullable&lt;T&gt;，则返回T，否则返回自身
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Type GetNonNullableType(this Type type)
        {
            if (IsNullableType(type))
                return Nullable.GetUnderlyingType(type);
            return type;
        }

        public static Type GetNullableType(this Type type)
        {
            if (type.IsValueType && !type.IsNullableType())
            {
                return typeof(Nullable<>).MakeGenericType(new Type[]
                {
                    type
                });
            }

            return type;
        }

        /// <summary>
        ///     是否为可空的target类型
        /// </summary>
        public static bool IsNullableOf(this Type type, Type target)
        {
            return type.IsNullableType() && Nullable.GetUnderlyingType(type) == target;
        }
        /// <summary>
        /// 判断是否为目标泛型类
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="genericType">Type of the generic.</param>
        /// <returns><c>true</c> if [is of generic type] [the specified generic type]; otherwise, <c>false</c>.</returns>
        public static bool IsOfGenericType(this Type type, Type genericType)
        {
            Type[] args;
            return type.TryGetGenericArguments(genericType, out args);
        }

        /// <summary>
        ///     判断是否为集合类型
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if the specified type is enumerable; otherwise, <c>false</c>.</returns>
        public static bool IsEnumerable(this Type type)
        {
            if (type.IsGenericType)
            {
                if (type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
                    return true;
            }
            else
            {
                if (type.GetInterfaces().Contains(typeof(IEnumerable)))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// 判断当前类型实例是否可以分配给T(基)类型实例
        /// Determines whether the specified actual type is type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="actualType">The actual type.</param>
        /// <returns><c>true</c> if the specified actual type is type; otherwise, <c>false</c>.</returns>
        public static bool IsType<T>(this Type actualType)
        {
            return typeof(T).IsAssignableFrom(actualType);
        }

        /// <summary>
        /// 是否继承TBase
        /// </summary>
        /// <typeparam name="TBase">The type of the t base.</typeparam>
        /// <param name="type">The type.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool Inherits<TBase>(this Type type)
        {
            return typeof(TBase).IsAssignableFrom(type);
        }

        public static bool Inherits(this Type type, Type tbase)
        {
            return tbase.IsAssignableFrom(type);
        }

        public static bool Implements<TInterface>(this Type type)
        {
            return typeof(TInterface).IsAssignableFrom(type);
        }

        /// <summary>
        /// 获取所有基类
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="andSelf">if set to <c>true</c> [and self].</param>
        /// <returns>IEnumerable&lt;Type&gt;.</returns>
        public static IEnumerable<Type> GetBaseTypes(this Type type, bool andSelf = false)
        {
            if (andSelf) yield return type;
            while ((type = type.BaseType) != null)
                yield return type;
        }

        /// <returns>
        ///     <c>true</c> if the specified type is enumerable; otherwise, <c>false</c>.
        /// </returns>
        public static bool TryGetGenericArguments(this Type type, Type genericType, out Type[] genericArgType)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));
            if (genericType == null) throw new ArgumentNullException(nameof(genericType));
            if (genericType.IsGenericType == false) throw new ArgumentException("genericType must be a generic type");
            Func<Type, Type, Type[]> checkGenericType = (t1, t2) =>
            {
                if (t1.IsGenericType)
                {
                    var def = t1.GetGenericTypeDefinition();
                    if (def == t2)
                        return t1.GetGenericArguments();
                }
                return null;
            };
            genericArgType = checkGenericType(type, genericType);
            if (genericArgType != null)
                return true;

            if (genericType.IsInterface)
                foreach (var @interface in type.GetInterfaces())
                {
                    genericArgType = checkGenericType(@interface, genericType);
                    if (genericArgType != null) return true;
                }
            else
                while (type.BaseType != null && type.BaseType != typeof(object))
                {
                    genericArgType = checkGenericType(type.BaseType, genericType);
                    if (genericArgType != null) return true;
                    type = type.BaseType;
                }

            return false;
        }

        #endregion Type

        #region Method        
        /// <summary>
        /// 判断方法的参数类型是否是指定类型
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="parameterTypes">The parameter types.</param>
        /// <returns><c>true</c> if the specified parameter types has parameters; otherwise, <c>false</c>.</returns>
        public static bool HasParameters(this MethodInfo method, params Type[] parameterTypes)
        {
            var methodParameters = method.GetParameters().Select(m => m.ParameterType).ToArray();
            if (methodParameters.Length != parameterTypes.Length) return false;
            for (var i = 0; i < methodParameters.Length; i++)
                if (!methodParameters[i].ToString().Equals(parameterTypes[i].ToString()))
                    return false;

            return true;
        }
        /// <summary>
        /// 获取类型的所有的方法，包括所有的方法
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>IEnumerable&lt;MethodInfo&gt;.</returns>
        public static IEnumerable<MethodInfo> GetAllMethods(this Type type)
        {
            var allTypes = type.GetInterfaces().ToList();
            allTypes.Add(type);

            return allTypes.SelectMany(m => m.GetMethods());
        }

        /// <summary>
        /// 根据方法名和方法参数类型获取泛型方法
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="name">The name.</param>
        /// <param name="parameterTypes">The parameter types.</param>
        /// <returns>MethodInfo.</returns>
        public static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes)
        {
            var methods = type.GetMethods().Where(method => method.Name == name);

            foreach (var method in methods)
            {
                if (method.HasParameters(parameterTypes))
                    return method;
            }

            return null;
        }

        #endregion Method

        #region Member        
        /// <summary>
        /// 从类型成员获取指定Attribute特性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo">The member information.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>T.</returns>
        public static T GetCustomAttribute<T>(this MemberInfo memberInfo, bool inherit)
            where T : Attribute
        {
            return memberInfo.GetCustomAttributes<T>(inherit).FirstOrDefault();
        }

        /// <summary>
        /// 从类型成员获取指定Attribute特性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo">The member information.</param>
        /// <param name="inherit">if set to <c>true</c> [inherited].</param>
        /// <returns>IEnumerable&lt;T&gt;.</returns>
        public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo memberInfo, bool inherit)
            where T : Attribute
        {
            return memberInfo.GetCustomAttributes(typeof(T), inherit).OfType<T>();
        }

        /// <summary>
        /// 检查指定指定类型成员中是否存在指定的Attribute特性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo">The member information.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool HasCustomAttribute<T>(this MemberInfo memberInfo, bool inherit) where T : Attribute
        {
            return memberInfo.GetCustomAttributes<T>(inherit).Any();
        }
        #endregion
    }
}